library(tidyverse)
library(plotly)
load("wireless.rda")
plot(x=wireless$x, y=wireless$y)
points(x=AP$x,y=AP$y, col="red", cex=1)

# exploring relationship between distance and signal strength
# without the loss of generality, use X
ap1 = as.numeric(AP[1,])
distances = apply(wireless[,1:2], 1, function(x){sqrt(sum((x - ap1)^2))})
par(mfrow=c(2,2))
plot(y=distances[distances > cutoff]^2, x=-wireless$S1[distances > cutoff])
plot(x=log(-wireless$S1[distances > cutoff]), y=2*log(distances[distances > cutoff]))
plot(y=distances, x=-wireless$S1)
plot(x=log(-wireless$S1[distances > cutoff]), y=log(distances[distances > cutoff]))

mod = lm(wireless$S2~log(distances2))
summary(mod)

Call:
lm(formula = wireless$S2 ~ log(distances2))

Residuals:
     Min       1Q   Median       3Q      Max 
-17.6911  -3.9331  -0.2897   3.8617  17.3204 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)       4.2900     2.5687    1.67   0.0961 .  
log(distances2) -18.1938     0.5488  -33.15   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 5.86 on 252 degrees of freedom
Multiple R-squared:  0.8135,    Adjusted R-squared:  0.8127 
F-statistic:  1099 on 1 and 252 DF,  p-value: < 2.2e-16

TODO: try to do that for S2, S3… as well

# exploring relationship between distance and signal strength
# without the loss of generality, use X
ap2 = as.numeric(AP[2,])
distances2 = apply(wireless[,1:2], 1, function(x){sqrt(sum((x - ap2)^2))})
par(mfrow=c(2,2))
plot(y=(distances2[distances2 < cutoff])^2, x=-wireless$S2[distances2 < cutoff])
plot(x=log(-wireless$S2[distances2 < cutoff]), y=2*log(distances2[distances2 < cutoff]))
plot(y=distances2[distances2 < cutoff], x=-wireless$S2[distances2 < cutoff])
plot(x=log(-wireless$S2[distances2 < cutoff]), y=log(distances2[distances2 < cutoff]))

plot(x=(distances2)[distances2 < cutoff], y=-wireless$S2[distances2 < cutoff])

#for distance above 100, the linear relationship between signal and distance breaks
#lots of points have -92 the worse signal ever 
mod2 = lm(wireless$S2[distances2 < cutoff]~distances2[distances2 < cutoff])
mod1 = lm(wireless$S1[distances < cutoff]~distances[distances < cutoff])
dx = (wireless[knn_predictions[223],4] -wireless[223,4])/mod$coefficients[2] * 
  (wireless[223,4]-mod$coefficients[1])/mod$coefficients[2] /
  (wireless[knn_predictions[223],1]-AP$x[2])
dy = (wireless[knn_predictions[223],4] -wireless[223,4])/mod$coefficients[2] * 
  (wireless[223,4]-mod$coefficients[1])/mod$coefficients[2] /
  (wireless[knn_predictions[223],2]-AP$y[2])
1/test2
             x          y
[1,] -1.266179 -26.674175
[2,]  3.147891  -5.036626
[3,]  4.904649   2.239919
[4,] -1.533693   4.114786
[5,] -1.273203   8.885615
k = mod2$coefficients[2]
b = mod2$coefficients[1]
signals = wireless_feature[224,]
test = t(apply(AP, 1, function(x) {as.numeric(wireless[224,1:2]) - x}))
test2 = apply(test, 2, function(x) as.numeric(k^2/(signals - b)) * x)
df_mod = lm(as.numeric(wireless_feature[223,] - wireless_feature[224,])~0 + 
                         test2[,1] + test2[,2])
summary(df_mod)

Call:
lm(formula = as.numeric(wireless_feature[223, ] - wireless_feature[224, 
    ]) ~ 0 + test2[, 1] + test2[, 2])

Residuals:
      1       2       3       4       5 
 0.2427  5.0153  1.8677 -0.5638  2.7374 

Coefficients:
           Estimate Std. Error t value Pr(>|t|)
test2[, 1]   -2.071      2.681  -0.773    0.496
test2[, 2]   -3.237      6.455  -0.502    0.651

Residual standard error: 3.489 on 3 degrees of freedom
Multiple R-squared:  0.1886,    Adjusted R-squared:  -0.3523 
F-statistic: 0.3487 on 2 and 3 DF,  p-value: 0.7308
##exploring using differentials 
##
sample_index = sample(1:nrow(wireless), 1)
sample_point = wireless[sample_index,]
sample_diff = data.frame(t(apply(wireless[-sample_index,], 1, function(x) x - as.numeric(wireless[sample_index,]))))
sample_diff_y = sample_diff[sample_diff$y == 0,]
mod = lm(x~.-y, data=sample_diff_y)
summary(mod)

Call:
lm(formula = x ~ . - y, data = sample_diff_y)

Residuals:
     Min       1Q   Median       3Q      Max 
-25.7877  -7.6092  -0.1254   9.2944  19.8359 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   0.6899     8.1992   0.084   0.9336    
S1           -1.3331     0.6897  -1.933   0.0638 .  
S2            3.7611     0.4953   7.593 3.62e-08 ***
S3           -1.5752     1.0351  -1.522   0.1397    
S4           -0.7039     0.9135  -0.771   0.4477    
S5            0.7194     0.4555   1.579   0.1259    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 11.43 on 27 degrees of freedom
Multiple R-squared:  0.9664,    Adjusted R-squared:  0.9602 
F-statistic: 155.3 on 5 and 27 DF,  p-value: < 2.2e-16
avg_error
[1] 17.20438
plot(wireless$x, wireless$y)
points(basic_x$fitted.values, basic_y$fitted.values, col="red")
segments(wireless$x, wireless$y, basic_x$fitted.values, basic_y$fitted.values, col="blue")

n = nrow(wireless)
#train_percent = 0.6
#sample_indices = sample(1:nrow(wireless), train_percent*n)
knn_predictions = numeric(n)
wireless_feature = wireless[,3:7]
pwdistances = as.matrix(dist(wireless_feature))
for (i in 1:n) {
  knn_predictions[i] = (1:n)[-i][which.min(as.matrix(pdist::pdist(wireless_feature[i,], wireless_feature[-i,])))]
}
knn_predictions
  [1]  24  23   6 156   6 181 101  37  36  11  10  10  14 182  14  86  86  16  18  24  19 102   2  25  24  27  26  30  31  28
 [31]  32  31  31  33 186  37  36  49  43 253  49  46  39 253  49  42  79  83  41  51  50  80  65  57  56  54  54  59  60  59
 [61]  62  61  64  63  53  93  66  63  46  71  70  83  74  73  74  77  81  77  76  52  77 204  72  95 106 107 147 108 119 100
 [91]  74  65  66 103  84 106 107 108  89  90 239  22  94 240  95  85  86  98 119 120 127 144  33  29 161 135  97  98  89 110
[121] 175 127 144 149 153 133 166 130 160 128 154 133 126 135 134 135 138 137 138 165 165 131 123 123 147 147 146 150 124 148
[151] 152 151 125 131 153 157 156 159 160 129 129 163 162 135 140 127 146 177 173 166 176 168 169 191 179 191 168 188 175 191
[181]   6  10  86  27  99  35 195 178 234 179 174 178 198 221 187 212 249 241 219 204 238 233 233 236 222 207 206 206 211 211
[211] 209 213 212 196 217 222 215 217 217 247 194 216 224 223 226 225 229 241 227 229 244 244 203 235 234 204 213 201 101 104
[241] 228 247  44 232 249 250 242 252 197 246 254 248  40 251
knn_x = wireless$x[knn_predictions]
knn_y = wireless$y[knn_predictions]
knn_avg_error = mean(sqrt((wireless$x - knn_x)^2 + (wireless$y - knn_y)^2))
knn_avg_error
[1] 10.97828
par(mfrow=c(1,2))
plot(density(sqrt((wireless$x - knn_x)^2 + (wireless$y - knn_y)^2)),
     main = "knn performance")
plot(density(sqrt((wireless$x - basic_x$fitted.values)^2 + (wireless$y - basic_y$fitted.values)^2)),
     main = "regression performance")

knn_errors = sqrt((wireless$x - knn_x)^2 + (wireless$y - knn_y)^2)
plot(wireless$x, wireless$y)
points(knn_x, knn_y, col="red")
segments(wireless$x, wireless$y, knn_x, knn_y, col="blue")

num_iter = 1000
coef_x = matrix(-1, nrow=num_iter, ncol=5)
coef_y = matrix(-1, nrow=num_iter, ncol=5)
singularities = 0
for (i in 1:num_iter) {
  Error = TRUE
  while (Error) {
    tryCatch(
      {
        sample_indices = sample(1:n,5)
        temp_A = wireless_feature[sample_indices,]
        temp_bx = wireless$x[sample_indices]
        temp_by = wireless$y[sample_indices]
        temp_x = solve(temp_A, temp_bx)
        temp_y = solve(temp_A, temp_by)
      Error = FALSE
      },
      error=function(cond) {
      }
    )}
  coef_x[i,] = temp_x 
  coef_y[i,] = temp_y 
}
dx = c(
  mean(coef_x[,1][abs(coef_x[,1]) < 20]),
  mean(coef_x[,2][abs(coef_x[,2]) < 20]),
  mean(coef_x[,2][abs(coef_x[,3]) < 20]),
  mean(coef_x[,2][abs(coef_x[,4]) < 20]),
  mean(coef_x[,2][abs(coef_x[,5]) < 20])
)
dy = c(
  mean(coef_y[,1][abs(coef_y[,1]) < 20]),
  mean(coef_y[,2][abs(coef_y[,2]) < 20]),
  mean(coef_y[,2][abs(coef_y[,3]) < 20]),
  mean(coef_y[,2][abs(coef_y[,4]) < 20]),
  mean(coef_y[,2][abs(coef_y[,5]) < 20])
)
df_avg_error
[1] 13.66296
set.seed(12345)
sample_indices = sample(1:254, 50)
plot(wireless$x[sample_indices], wireless$y[sample_indices], ylim=c(0, 145), xlim=c(10, 235))
points((knn_x-df_x)[sample_indices], (knn_y-df_y)[sample_indices], col="red")
points(knn_x[sample_indices], knn_y[sample_indices], col="yellow")
segments(wireless$x[sample_indices], wireless$y[sample_indices], knn_x[sample_indices], knn_y[sample_indices], col="blue")
segments(knn_x[sample_indices], knn_y[sample_indices], (knn_x-df_x)[sample_indices], (knn_y-df_y)[sample_indices], col="green")

#segments(wireless$x, wireless$y, knn_x+df_x, knn_y+ df_y, col="green")
p = plot_ly(wireless, x=~x, y=~y, name = "receivers", type="scatter", 
            mode="markers", text=paste(1:254, "<br>", hover_text)) %>% 
  add_trace(x=AP$x, y=AP$y, name = "wifi post", mode="markers", text=rownames(AP))
p
kclusters = kmeans(wireless[,3:7], 5)
#kclusters$cluster

ggplot(data=wireless) +
  geom_point(aes(x=x,y=y), colour=kclusters$cluster) 
ggplot(data=wireless) +
  geom_point(aes(x=x,y=y)) +
  scale_fill_manual(kclusters$cluster)
cutoff = 68
# seems like 70 is a good cut off lets check how many points have more than 70
wireless_strong = wireless %>%
  mutate(S1 = S1 > -cutoff) %>% 
  mutate(S2 = S2 > -cutoff) %>% 
  mutate(S3 = S3 > -cutoff) %>% 
  mutate(S4 = S4 > -cutoff) %>% 
  mutate(S5 = S5 > -cutoff) 

# seems like 70 is not a good cutoff as we think
table(apply(wireless_strong[,3:7], 1, sum))
bad_locations = wireless_strong[as.numeric(apply(wireless_strong[,3:7], 1, sum)) < 2,]

plot(x=bad_locations$x, y=bad_locations$y, ylim=c(0,150), xlim=c(0,230))
points(x=AP$x,y=AP$y, col="red", cex=5)
View(data.frame(table(wireless$y)))
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3IgbGlicmFyeX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGxvdGx5KQpgYGAKCgpgYGB7ciBpbXBvcnRfZGF0YX0KbG9hZCgid2lyZWxlc3MucmRhIikKYGBgCgpgYGB7ciBzZWVpbmdfcGxhY2VzfQpwbG90KHg9d2lyZWxlc3MkeCwgeT13aXJlbGVzcyR5KQpwb2ludHMoeD1BUCR4LHk9QVAkeSwgY29sPSJyZWQiLCBjZXg9MSkKYGBgCgpgYGB7cn0KIyBleHBsb3JpbmcgcmVsYXRpb25zaGlwIGJldHdlZW4gZGlzdGFuY2UgYW5kIHNpZ25hbCBzdHJlbmd0aAojIHdpdGhvdXQgdGhlIGxvc3Mgb2YgZ2VuZXJhbGl0eSwgdXNlIFgKYXAxID0gYXMubnVtZXJpYyhBUFsxLF0pCmRpc3RhbmNlcyA9IGFwcGx5KHdpcmVsZXNzWywxOjJdLCAxLCBmdW5jdGlvbih4KXtzcXJ0KHN1bSgoeCAtIGFwMSleMikpfSkKCnBhcihtZnJvdz1jKDIsMikpCnBsb3QoeT0oZGlzdGFuY2VzW2Rpc3RhbmNlcyA8IGN1dG9mZl0pXjIsIHg9LXdpcmVsZXNzJFMxW2Rpc3RhbmNlcyA8IGN1dG9mZl0pCgpwbG90KHg9bG9nKC13aXJlbGVzcyRTMVtkaXN0YW5jZXMgPCBjdXRvZmZdKSwgeT0yKmxvZyhkaXN0YW5jZXNbZGlzdGFuY2VzIDwgY3V0b2ZmXSkpCgpwbG90KHk9ZGlzdGFuY2VzW2Rpc3RhbmNlcyA8IGN1dG9mZl0sIHg9LXdpcmVsZXNzJFMxW2Rpc3RhbmNlcyA8IGN1dG9mZl0pCgpwbG90KHg9bG9nKC13aXJlbGVzcyRTMVtkaXN0YW5jZXMgPCBjdXRvZmZdKSwgeT1sb2coZGlzdGFuY2VzW2Rpc3RhbmNlcyA8IGN1dG9mZl0pKQpgYGAKCgpgYGB7cn0KbW9kID0gbG0od2lyZWxlc3MkUzJ+bG9nKGRpc3RhbmNlczIpKQpzdW1tYXJ5KG1vZCkKYGBgCgpUT0RPOgp0cnkgdG8gZG8gdGhhdCBmb3IgUzIsIFMzLi4uIGFzIHdlbGwKCmBgYHtyfQojIGV4cGxvcmluZyByZWxhdGlvbnNoaXAgYmV0d2VlbiBkaXN0YW5jZSBhbmQgc2lnbmFsIHN0cmVuZ3RoCiMgd2l0aG91dCB0aGUgbG9zcyBvZiBnZW5lcmFsaXR5LCB1c2UgWAphcDIgPSBhcy5udW1lcmljKEFQWzIsXSkKZGlzdGFuY2VzMiA9IGFwcGx5KHdpcmVsZXNzWywxOjJdLCAxLCBmdW5jdGlvbih4KXtzcXJ0KHN1bSgoeCAtIGFwMileMikpfSkKCnBhcihtZnJvdz1jKDIsMikpCnBsb3QoeT0oZGlzdGFuY2VzMltkaXN0YW5jZXMyIDwgY3V0b2ZmXSleMiwgeD0td2lyZWxlc3MkUzJbZGlzdGFuY2VzMiA8IGN1dG9mZl0pCgpwbG90KHg9bG9nKC13aXJlbGVzcyRTMltkaXN0YW5jZXMyIDwgY3V0b2ZmXSksIHk9Mipsb2coZGlzdGFuY2VzMltkaXN0YW5jZXMyIDwgY3V0b2ZmXSkpCgpwbG90KHk9ZGlzdGFuY2VzMltkaXN0YW5jZXMyIDwgY3V0b2ZmXSwgeD0td2lyZWxlc3MkUzJbZGlzdGFuY2VzMiA8IGN1dG9mZl0pCgpwbG90KHg9bG9nKC13aXJlbGVzcyRTMltkaXN0YW5jZXMyIDwgY3V0b2ZmXSksIHk9bG9nKGRpc3RhbmNlczJbZGlzdGFuY2VzMiA8IGN1dG9mZl0pKQpgYGAKCmBgYHtyfQpwbG90KHg9KGRpc3RhbmNlczIpW2Rpc3RhbmNlczIgPCBjdXRvZmZdLCB5PS13aXJlbGVzcyRTMltkaXN0YW5jZXMyIDwgY3V0b2ZmXSkKI2ZvciBkaXN0YW5jZSBhYm92ZSAxMDAsIHRoZSBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gc2lnbmFsIGFuZCBkaXN0YW5jZSBicmVha3MKI2xvdHMgb2YgcG9pbnRzIGhhdmUgLTkyIHRoZSB3b3JzZSBzaWduYWwgZXZlciAKYGBgCgpgYGB7cn0KbW9kMiA9IGxtKHdpcmVsZXNzJFMyW2Rpc3RhbmNlczIgPCBjdXRvZmZdfmRpc3RhbmNlczJbZGlzdGFuY2VzMiA8IGN1dG9mZl0pCm1vZDEgPSBsbSh3aXJlbGVzcyRTMVtkaXN0YW5jZXMgPCBjdXRvZmZdfmRpc3RhbmNlc1tkaXN0YW5jZXMgPCBjdXRvZmZdKQoKZHggPSAod2lyZWxlc3Nba25uX3ByZWRpY3Rpb25zWzIyM10sNF0gLXdpcmVsZXNzWzIyMyw0XSkvbW9kJGNvZWZmaWNpZW50c1syXSAqIAogICh3aXJlbGVzc1syMjMsNF0tbW9kJGNvZWZmaWNpZW50c1sxXSkvbW9kJGNvZWZmaWNpZW50c1syXSAvCiAgKHdpcmVsZXNzW2tubl9wcmVkaWN0aW9uc1syMjNdLDFdLUFQJHhbMl0pCgpkeSA9ICh3aXJlbGVzc1trbm5fcHJlZGljdGlvbnNbMjIzXSw0XSAtd2lyZWxlc3NbMjIzLDRdKS9tb2QkY29lZmZpY2llbnRzWzJdICogCiAgKHdpcmVsZXNzWzIyMyw0XS1tb2QkY29lZmZpY2llbnRzWzFdKS9tb2QkY29lZmZpY2llbnRzWzJdIC8KICAod2lyZWxlc3Nba25uX3ByZWRpY3Rpb25zWzIyM10sMl0tQVAkeVsyXSkKYGBgCgpgYGB7cn0KQSA9IG1vZDEkY29lZmZpY2llbnRzWzJdXjIgKgogIHJiaW5kKCh3aXJlbGVzc1syMjQsMToyXSAtIEFQWzEsXSkvKHdpcmVsZXNzWzIyNCwgM10gLSAtbW9kMiRjb2VmZmljaWVudHNbMV0pLAogICAgICAod2lyZWxlc3NbMjI0LDE6Ml0gLSBBUFsyLF0pLyh3aXJlbGVzc1syMjQsIDRdIC0gLW1vZDIkY29lZmZpY2llbnRzWzFdKSkKCmIgPSB3aXJlbGVzc1syMjQsIDM6NF0gLSB3aXJlbGVzc1syMjMsIDM6NF0KCnNvbHZlKEEsIGIpCgoKYGBgCgoKYGBge3J9CmsgPSBtb2QyJGNvZWZmaWNpZW50c1syXQpiID0gbW9kMiRjb2VmZmljaWVudHNbMV0KCnNpZ25hbHMgPSB3aXJlbGVzc19mZWF0dXJlWzIyNCxdCgp0ZXN0ID0gdChhcHBseShBUCwgMSwgZnVuY3Rpb24oeCkge2FzLm51bWVyaWMod2lyZWxlc3NbMjI0LDE6Ml0pIC0geH0pKQoKdGVzdDIgPSBhcHBseSh0ZXN0LCAyLCBmdW5jdGlvbih4KSBhcy5udW1lcmljKGteMi8oc2lnbmFscyAtIGIpKSAqIHgpCgpkZl9tb2QgPSBsbShhcy5udW1lcmljKHdpcmVsZXNzX2ZlYXR1cmVbMjIzLF0gLSB3aXJlbGVzc19mZWF0dXJlWzIyNCxdKX4wICsgCiAgICAgICAgICAgICAgICAgICAgICAgICB0ZXN0MlssMV0gKyB0ZXN0MlssMl0pCgpzdW1tYXJ5KGRmX21vZCkKYGBgCgoKYGBge3J9CiMjZXhwbG9yaW5nIHVzaW5nIGRpZmZlcmVudGlhbHMgCiMjCgpzYW1wbGVfaW5kZXggPSBzYW1wbGUoMTpucm93KHdpcmVsZXNzKSwgMSkKc2FtcGxlX3BvaW50ID0gd2lyZWxlc3Nbc2FtcGxlX2luZGV4LF0Kc2FtcGxlX2RpZmYgPSBkYXRhLmZyYW1lKHQoYXBwbHkod2lyZWxlc3NbLXNhbXBsZV9pbmRleCxdLCAxLCBmdW5jdGlvbih4KSB4IC0gYXMubnVtZXJpYyh3aXJlbGVzc1tzYW1wbGVfaW5kZXgsXSkpKSkKc2FtcGxlX2RpZmZfeSA9IHNhbXBsZV9kaWZmW3NhbXBsZV9kaWZmJHkgPT0gMCxdCgptb2QgPSBsbSh4fi4teSwgZGF0YT1zYW1wbGVfZGlmZl95KQpzdW1tYXJ5KG1vZCkKYGBgCgpgYGB7cn0KYmFzaWNfeCA9IGxtKHh+Li15LCBkYXRhPXdpcmVsZXNzKQpiYXNpY195ID0gbG0oeX4uLXgsIGRhdGE9d2lyZWxlc3MpCgojc3VtbWFyeShiYXNpY194KQojc3VtbWFyeShiYXNpY195KQoKYXZnX2Vycm9yID0gbWVhbihzcXJ0KCh3aXJlbGVzcyR4IC0gYmFzaWNfeCRmaXR0ZWQudmFsdWVzKV4yICsgKHdpcmVsZXNzJHkgLSBiYXNpY195JGZpdHRlZC52YWx1ZXMpXjIpKQphdmdfZXJyb3IKYGBgCgpgYGB7ciBiYXNpY19wcmVkaWN0aW9ufQpwbG90KHdpcmVsZXNzJHgsIHdpcmVsZXNzJHkpCnBvaW50cyhiYXNpY194JGZpdHRlZC52YWx1ZXMsIGJhc2ljX3kkZml0dGVkLnZhbHVlcywgY29sPSJyZWQiKQpzZWdtZW50cyh3aXJlbGVzcyR4LCB3aXJlbGVzcyR5LCBiYXNpY194JGZpdHRlZC52YWx1ZXMsIGJhc2ljX3kkZml0dGVkLnZhbHVlcywgY29sPSJibHVlIikKYGBgCgpgYGB7ciBiYXNpY19rbm59Cm4gPSBucm93KHdpcmVsZXNzKQojdHJhaW5fcGVyY2VudCA9IDAuNgojc2FtcGxlX2luZGljZXMgPSBzYW1wbGUoMTpucm93KHdpcmVsZXNzKSwgdHJhaW5fcGVyY2VudCpuKQprbm5fcHJlZGljdGlvbnMgPSBudW1lcmljKG4pCgp3aXJlbGVzc19mZWF0dXJlID0gd2lyZWxlc3NbLDM6N10KcHdkaXN0YW5jZXMgPSBhcy5tYXRyaXgoZGlzdCh3aXJlbGVzc19mZWF0dXJlKSkKCmZvciAoaSBpbiAxOm4pIHsKICBrbm5fcHJlZGljdGlvbnNbaV0gPSAoMTpuKVstaV1bd2hpY2gubWluKGFzLm1hdHJpeChwZGlzdDo6cGRpc3Qod2lyZWxlc3NfZmVhdHVyZVtpLF0sIHdpcmVsZXNzX2ZlYXR1cmVbLWksXSkpKV0KfQpgYGAKCmBgYHtyfQprbm5feCA9IHdpcmVsZXNzJHhba25uX3ByZWRpY3Rpb25zXQprbm5feSA9IHdpcmVsZXNzJHlba25uX3ByZWRpY3Rpb25zXQoKa25uX2F2Z19lcnJvciA9IG1lYW4oc3FydCgod2lyZWxlc3MkeCAtIGtubl94KV4yICsgKHdpcmVsZXNzJHkgLSBrbm5feSleMikpCmtubl9hdmdfZXJyb3IKCnBhcihtZnJvdz1jKDEsMikpCnBsb3QoZGVuc2l0eShzcXJ0KCh3aXJlbGVzcyR4IC0ga25uX3gpXjIgKyAod2lyZWxlc3MkeSAtIGtubl95KV4yKSksCiAgICAgbWFpbiA9ICJrbm4gcGVyZm9ybWFuY2UiKQpwbG90KGRlbnNpdHkoc3FydCgod2lyZWxlc3MkeCAtIGJhc2ljX3gkZml0dGVkLnZhbHVlcyleMiArICh3aXJlbGVzcyR5IC0gYmFzaWNfeSRmaXR0ZWQudmFsdWVzKV4yKSksCiAgICAgbWFpbiA9ICJyZWdyZXNzaW9uIHBlcmZvcm1hbmNlIikKCmtubl9lcnJvcnMgPSBzcXJ0KCh3aXJlbGVzcyR4IC0ga25uX3gpXjIgKyAod2lyZWxlc3MkeSAtIGtubl95KV4yKQpgYGAKCgpgYGB7cn0KcGxvdCh3aXJlbGVzcyR4LCB3aXJlbGVzcyR5KQpwb2ludHMoa25uX3gsIGtubl95LCBjb2w9InJlZCIpCnNlZ21lbnRzKHdpcmVsZXNzJHgsIHdpcmVsZXNzJHksIGtubl94LCBrbm5feSwgY29sPSJibHVlIikKYGBgCgpgYGB7ciBleHBsb3JpbmdfZGlmZmVyZW50aWFsfQpkdGFibGUgPSB3aXJlbGVzc1trbm5fcHJlZGljdGlvbnMsXSAtIHdpcmVsZXNzCmNvbG5hbWVzKGR0YWJsZSkgPSBwYXN0ZTAoImQiLCBjb2xuYW1lcyh3aXJlbGVzcykpCgojIGRvZXNuJ3Qgd29yayBvdXQsIEkgdXNlIHRoZSB0cmFkaXRpb25hbCB3YXkKZHhfbW9kID0gbG0oZHh+Li1keSwgZGF0YT1kdGFibGUpCmR5X21vZCA9IGxtKGR5fi4tZHgsIGRhdGE9ZHRhYmxlKQpgYGAKCmBgYHtyfQpudW1faXRlciA9IDEwMDAKY29lZl94ID0gbWF0cml4KC0xLCBucm93PW51bV9pdGVyLCBuY29sPTUpCmNvZWZfeSA9IG1hdHJpeCgtMSwgbnJvdz1udW1faXRlciwgbmNvbD01KQpzaW5ndWxhcml0aWVzID0gMApmb3IgKGkgaW4gMTpudW1faXRlcikgewogIEVycm9yID0gVFJVRQogIHdoaWxlIChFcnJvcikgewogICAgdHJ5Q2F0Y2goCiAgICAgIHsKICAgICAgICBzYW1wbGVfaW5kaWNlcyA9IHNhbXBsZSgxOm4sNSkKICAgICAgICB0ZW1wX0EgPSB3aXJlbGVzc19mZWF0dXJlW3NhbXBsZV9pbmRpY2VzLF0KICAgICAgICB0ZW1wX2J4ID0gd2lyZWxlc3MkeFtzYW1wbGVfaW5kaWNlc10KICAgICAgICB0ZW1wX2J5ID0gd2lyZWxlc3MkeVtzYW1wbGVfaW5kaWNlc10KICAgICAgICB0ZW1wX3ggPSBzb2x2ZSh0ZW1wX0EsIHRlbXBfYngpCiAgICAgICAgdGVtcF95ID0gc29sdmUodGVtcF9BLCB0ZW1wX2J5KQogICAgICBFcnJvciA9IEZBTFNFCiAgICAgIH0sCiAgICAgIGVycm9yPWZ1bmN0aW9uKGNvbmQpIHsKICAgICAgfQogICAgKX0KICBjb2VmX3hbaSxdID0gdGVtcF94IAogIGNvZWZfeVtpLF0gPSB0ZW1wX3kgCn0KCmR4ID0gYygKICBtZWFuKGNvZWZfeFssMV1bYWJzKGNvZWZfeFssMV0pIDwgMjBdKSwKICBtZWFuKGNvZWZfeFssMl1bYWJzKGNvZWZfeFssMl0pIDwgMjBdKSwKICBtZWFuKGNvZWZfeFssMl1bYWJzKGNvZWZfeFssM10pIDwgMjBdKSwKICBtZWFuKGNvZWZfeFssMl1bYWJzKGNvZWZfeFssNF0pIDwgMjBdKSwKICBtZWFuKGNvZWZfeFssMl1bYWJzKGNvZWZfeFssNV0pIDwgMjBdKQopCgpkeSA9IGMoCiAgbWVhbihjb2VmX3lbLDFdW2Ficyhjb2VmX3lbLDFdKSA8IDIwXSksCiAgbWVhbihjb2VmX3lbLDJdW2Ficyhjb2VmX3lbLDJdKSA8IDIwXSksCiAgbWVhbihjb2VmX3lbLDJdW2Ficyhjb2VmX3lbLDNdKSA8IDIwXSksCiAgbWVhbihjb2VmX3lbLDJdW2Ficyhjb2VmX3lbLDRdKSA8IDIwXSksCiAgbWVhbihjb2VmX3lbLDJdW2Ficyhjb2VmX3lbLDVdKSA8IDIwXSkKKQpgYGAKCmBgYHtyfQpkZl94ID0gYXMubnVtZXJpYyhhcHBseShkdGFibGVbLDM6N10sIDEsIGZ1bmN0aW9uKHgpIHtzdW0oeCAqIGR4KX0pKQpkZl95ID0gYXMubnVtZXJpYyhhcHBseShkdGFibGVbLDM6N10sIDEsIGZ1bmN0aW9uKHgpIHtzdW0oeCAqIGR5KX0pKQoKZGZfZXJyb3IgPSBzcXJ0KChrbm5feC1kZl94IC0gd2lyZWxlc3MkeCleMiArIChrbm5feS1kZl95IC0gd2lyZWxlc3MkeSleMikKZGZfYXZnX2Vycm9yID0gbWVhbihkZl9lcnJvcikKCmRmX2F2Z19lcnJvcgpgYGAKCmBgYHtyfQpzZXQuc2VlZCgxMjM0NSkKc2FtcGxlX2luZGljZXMgPSBzYW1wbGUoMToyNTQsIDUwKQpwbG90KHdpcmVsZXNzJHhbc2FtcGxlX2luZGljZXNdLCB3aXJlbGVzcyR5W3NhbXBsZV9pbmRpY2VzXSwgeWxpbT1jKDAsIDE0NSksIHhsaW09YygxMCwgMjM1KSkKcG9pbnRzKGtubl94W3NhbXBsZV9pbmRpY2VzXSwga25uX3lbc2FtcGxlX2luZGljZXNdLCBjb2w9InllbGxvdyIpCnBvaW50cygoa25uX3gtZGZfeClbc2FtcGxlX2luZGljZXNdLCAoa25uX3ktZGZfeSlbc2FtcGxlX2luZGljZXNdLCBjb2w9InJlZCIpCgpzZWdtZW50cyh3aXJlbGVzcyR4W3NhbXBsZV9pbmRpY2VzXSwgd2lyZWxlc3MkeVtzYW1wbGVfaW5kaWNlc10sIGtubl94W3NhbXBsZV9pbmRpY2VzXSwga25uX3lbc2FtcGxlX2luZGljZXNdLCBjb2w9ImJsdWUiKQpzZWdtZW50cyhrbm5feFtzYW1wbGVfaW5kaWNlc10sIGtubl95W3NhbXBsZV9pbmRpY2VzXSwgKGtubl94LWRmX3gpW3NhbXBsZV9pbmRpY2VzXSwgKGtubl95LWRmX3kpW3NhbXBsZV9pbmRpY2VzXSwgY29sPSJncmVlbiIpCiNzZWdtZW50cyh3aXJlbGVzcyR4LCB3aXJlbGVzcyR5LCBrbm5feCtkZl94LCBrbm5feSsgZGZfeSwgY29sPSJncmVlbiIpCmBgYAoKCgpgYGB7ciBwbG90bHl9CnAgPSBwbG90X2x5KHdpcmVsZXNzLCB4PX54LCB5PX55LCBuYW1lID0gInJlY2VpdmVycyIsIHR5cGU9InNjYXR0ZXIiLCAKICAgICAgICAgICAgbW9kZT0ibWFya2VycyIsIHRleHQ9cGFzdGUoMToyNTQsICI8YnI+IiwgaG92ZXJfdGV4dCkpICU+JSAKICBhZGRfdHJhY2UoeD1BUCR4LCB5PUFQJHksIG5hbWUgPSAid2lmaSBwb3N0IiwgbW9kZT0ibWFya2VycyIsIHRleHQ9cm93bmFtZXMoQVApKQoKcApgYGAKCgpgYGB7cn0Ka2NsdXN0ZXJzID0ga21lYW5zKHdpcmVsZXNzWywzOjddLCA1KQoja2NsdXN0ZXJzJGNsdXN0ZXIKCmdncGxvdChkYXRhPXdpcmVsZXNzKSArCiAgZ2VvbV9wb2ludChhZXMoeD14LHk9eSksIGNvbG91cj1rY2x1c3RlcnMkY2x1c3RlcikgCmBgYAoKCmBgYHtyfQpnZ3Bsb3QoZGF0YT13aXJlbGVzcykgKwogIGdlb21fcG9pbnQoYWVzKHg9eCx5PXkpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwoa2NsdXN0ZXJzJGNsdXN0ZXIpCgpgYGAKCgpgYGB7cn0KY3V0b2ZmID0gNjgKIyBzZWVtcyBsaWtlIDcwIGlzIGEgZ29vZCBjdXQgb2ZmIGxldHMgY2hlY2sgaG93IG1hbnkgcG9pbnRzIGhhdmUgbW9yZSB0aGFuIDcwCndpcmVsZXNzX3N0cm9uZyA9IHdpcmVsZXNzICU+JQogIG11dGF0ZShTMSA9IFMxID4gLWN1dG9mZikgJT4lIAogIG11dGF0ZShTMiA9IFMyID4gLWN1dG9mZikgJT4lIAogIG11dGF0ZShTMyA9IFMzID4gLWN1dG9mZikgJT4lIAogIG11dGF0ZShTNCA9IFM0ID4gLWN1dG9mZikgJT4lIAogIG11dGF0ZShTNSA9IFM1ID4gLWN1dG9mZikgCgojIHNlZW1zIGxpa2UgNzAgaXMgbm90IGEgZ29vZCBjdXRvZmYgYXMgd2UgdGhpbmsKdGFibGUoYXBwbHkod2lyZWxlc3Nfc3Ryb25nWywzOjddLCAxLCBzdW0pKQpgYGAKCmBgYHtyfQpiYWRfbG9jYXRpb25zID0gd2lyZWxlc3Nfc3Ryb25nW2FzLm51bWVyaWMoYXBwbHkod2lyZWxlc3Nfc3Ryb25nWywzOjddLCAxLCBzdW0pKSA8IDIsXQoKcGxvdCh4PWJhZF9sb2NhdGlvbnMkeCwgeT1iYWRfbG9jYXRpb25zJHksIHlsaW09YygwLDE1MCksIHhsaW09YygwLDIzMCkpCnBvaW50cyh4PUFQJHgseT1BUCR5LCBjb2w9InJlZCIsIGNleD01KQpgYGAKCgpgYGB7cn0KVmlldyhkYXRhLmZyYW1lKHRhYmxlKHdpcmVsZXNzJHkpKSkKCmBgYAoK